package com.bilubi.reggie.reggie.aop;

import com.bilubi.reggie.reggie.annotation.AutoCleanRedis;
import com.bilubi.reggie.reggie.annotation.AutoFill;
import com.bilubi.reggie.reggie.common.BaseContext;
import com.bilubi.reggie.reggie.enums.OperateType;
import com.bilubi.reggie.reggie.enums.RedisDataType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Set;

@Slf4j
@Aspect
@Component
public class AutoFillAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    @Pointcut("execution(* com.bilubi.reggie.reggie.mapper.*.*(..)) && @annotation(com.bilubi.reggie.reggie.annotation.AutoFill)")
    public void autoFillPointcut() {}

    @Pointcut("execution(* com.bilubi.reggie.reggie.mapper.*.*(..)) && @annotation(com.bilubi.reggie.reggie.annotation.AutoCleanRedis)")
    public void autoCleanRedisPointCut(){}

    @Before("autoFillPointcut()")
    public void autoFill(JoinPoint joinPoint) {

        //获取方法签名对象
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //获取注解对象
        AutoFill autoFill = methodSignature.getMethod().getAnnotation(AutoFill.class);
        //获取数据库操作类型
        OperateType[] operateType = autoFill.value();

        //获取拦截方法实体类对象参数
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return;
        }

        Object entity = args[0];

        //获取参数类型
        Class<?> entityClass = entity.getClass();

        //获取填充数据
        Long createUserId = BaseContext.getValue();// 当前用户名
        Date now = new Date();// 当前时间
        log.info("时间：{}", now.toString());

        for (OperateType operateType_ : operateType) {
            //为插入操作填充数据
            if (operateType_ == OperateType.INSERT) {
                try {
                    if (List.class.isAssignableFrom(entityClass)) {
                        for (Object object : (List<?>) entity) {
                            Method setCreateUser = object.getClass()
                                    .getMethod("setCreateUser", Long.class);
                            Method setUpdateUser = object.getClass()
                                    .getMethod("setUpdateUser", Long.class);
                            Method setCreateTime = object.getClass()
                                    .getMethod("setCreateTime", Date.class);
                            Method setUpdateTime = object.getClass()
                                    .getMethod("setUpdateTime", Date.class);

                            setCreateUser.invoke(object, createUserId);
                            setUpdateUser.invoke(object, createUserId);
                            setCreateTime.invoke(object, now);
                            setUpdateTime.invoke(object, now);
                        }
                    }
                    else {
                        Method setCreateUser = entity.getClass()
                                .getMethod("setCreateUser", Long.class);
                        Method setUpdateUser = entity.getClass()
                                .getMethod("setUpdateUser", Long.class);
                        Method setCreateTime = entity.getClass()
                                .getMethod("setCreateTime", Date.class);
                        Method setUpdateTime = entity.getClass()
                                .getMethod("setUpdateTime", Date.class);

                        setCreateUser.invoke(entity, createUserId);
                        setUpdateUser.invoke(entity, createUserId);
                        setCreateTime.invoke(entity, now);
                        setUpdateTime.invoke(entity, now);
                    }
                } catch (Exception ex) {
                    throw new RuntimeException();
                }
            }

            //为更新操作填充数据
            if (operateType_ == OperateType.UPDATE) {
                try {
                    if (List.class.isAssignableFrom(entityClass)) {
                        for (Object object : (List<?>) entity) {
                            Method setUpdateUser = object.getClass()
                                    .getMethod("setUpdateUser", Long.class);
                            Method setUpdateTime = object.getClass()
                                    .getMethod("setUpdateTime", Date.class);

                            setUpdateUser.invoke(object, createUserId);
                            setUpdateTime.invoke(object, now);
                        }
                    }
                    else {
                        Method setUpdateUser = entity.getClass()
                                .getMethod("setUpdateUser", Long.class);
                        Method setUpdateTime = entity.getClass()
                                .getMethod("setUpdateTime", Date.class);

                        setUpdateUser.invoke(entity, createUserId);
                        setUpdateTime.invoke(entity, now);
                    }
                } catch (Exception ex) {
                    throw new RuntimeException();
                }
            }

            //为user对象填充uesrID
            if (operateType_ == OperateType.USERID){
                try {
                    Method method = entityClass.getMethod("setUserId", Long.class);
                    method.invoke(entity, createUserId);
                }
                catch (Exception ex) {
                    throw new RuntimeException();
                }
            }
        }
    }

    @After("autoCleanRedisPointCut()")
    public void autoCleanRedis(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        AutoCleanRedis autoCleanRedis = methodSignature.getMethod().getAnnotation(AutoCleanRedis.class);
        RedisDataType[] redisDataType = autoCleanRedis.value();
        for (RedisDataType redisDataType_ : redisDataType) {
            if (redisDataType_.equals(RedisDataType.DISH)){
                String pattern = "dish_*";
                Set<String> keys = redisTemplate.keys(pattern);
                redisTemplate.delete(keys);
            }
            else {
                String pattern = "setmeal_*";
                redisTemplate.delete(pattern);
            }
        }
    }
}

